import fetch from 'isomorphic-fetch'

import appendQuery from 'append-query'
import queryString from 'query-string'
import RequestError from './RequestError'

const defaultHTTPHeaders = {
  Accept: 'application/json',
  'Content-Type': 'application/json',
}
const ajax = (
  method,
  path,
  body,
  { headers = defaultHTTPHeaders, form = false } = {},
) => {
  let url = path
  const fetchConfig = {
    method,
    headers,
    body,
    credentials: 'include',
  }

  if (method !== 'get' && method !== 'delete') {
    if (!form) {
      fetchConfig.body = JSON.stringify(body)
    } else {
      fetchConfig.body = queryString.stringify(body)

      fetchConfig.headers = {
        ...headers,
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
      }
    }
  }

  if (method === 'get') {
    /** FireFox ajax get request can not have a body, so let it be `null`  */
    fetchConfig.body = null
    url = appendQuery(path, { ...body, _: new Date().getTime() })
  }

  function handleFullfil(resolve, reject, response) {
    const { status, statusText, ok } = response

    response.json().then(payload => {
      // The `ok` read-only property of the Response interface contains a boolean stating
      // whether the response was successful (status in the range **200-299**) or not.
      if (ok) {
        resolve({ payload })
      } else {
        // If it's not `ok` then read the `errors` from the response
        const { errors, error } = payload

        reject(new RequestError(status, statusText, errors || error))
      }
    })
  }

  function handleReject(reject, error) {
    reject(error)
  }

  return new Promise((resolve, reject) =>
    fetch(url, fetchConfig).then(
      handleFullfil.bind(null, resolve, reject),
      handleReject.bind(null, reject),
    ),
  )
}

export default ['get', 'post', 'put', 'patch', 'delete', 'head'].reduce(
  (api, method) => ({
    ...api,
    [method]: ajax.bind(null, method),
  }),
  {},
)
